package util;

import static java.lang.Math.pow;
import static java.lang.Math.sqrt;
import ij.IJ;
import ij.ImagePlus;
import ij.ImageStack;
import ij.gui.NewImage;
import ij.process.ImageProcessor;

import java.util.ArrayList;
import java.util.Arrays;


/**
 *  This class holds various utility methods for all the plugins
 */
public class QualityControlHelper {
    

    /**
     * Returns the gradient magnitude of a given profile at index i
     * 
     * @param prof
     * @param i
     * @return
     */
    public static double gradientMagnitude(double[] prof, int i) {
        if (i == 0) {
            return sqrt(pow(prof[i + 1] - prof[i], 2)) / sqrt(2);
        } else if (i == (prof.length - 1)) {
            return sqrt(pow(prof[i] - prof[i - 1], 2)) / sqrt(2);
        }
        return sqrt(pow(prof[i + 1] - prof[i], 2) + pow(prof[i] - prof[i - 1], 2)) / sqrt(2);
    }

    /**
     * Returns the laplacian of a given profile at index i
     * 
     * @param prof
     * @param i
     * @return
     */
    public static double laplacian(double[] prof, int i) {
        if (i == (prof.length - 1)) {
            return prof[i] - 2 * prof[i];
        }
        return prof[i + 1] + prof[i] - 2 * prof[i];
    }

    /**
     * 
     */

    public static ImagePlus getMedianImageFromStack(ImageStack stack, String filename) {
        int width = stack.getWidth();
        int height = stack.getHeight(); 
        int stackSize = stack.getSize();
        if (filename == null || filename.isEmpty()) {
            //get the first slice's label.  slices start at index 1 (instead of 0)
            filename = stack.getSliceLabel(1) + "_MedianImage"; 
        } else {
            filename += "_MedianImage";
        }
        ImagePlus resultImg = NewImage.createByteImage(filename, width, height, 1, NewImage.FILL_BLACK);
        ArrayList<byte[]> medianLists = new ArrayList<byte[]>(width * height);
        byte[] curSlice = new byte[width * height];

        // TODO: speed improvement here would be to use a min-max-median heap to
        // store
        // the values instead of a 2D array that is sorted later
        int offset, i;
        for (int n = 1; n <= stackSize; n++) {
            curSlice = (byte[]) stack.getPixels(n);
            for (int y = 0; y < height; y++) {
                offset = y * width;
                for (int x = 0; x < width; x++) {
                    i = offset + x;
                    if (medianLists.size() < width * height) {
                        medianLists.add(new byte[stackSize]);
                    }
                    (medianLists.get(i))[n - 1] = curSlice[i];
                }
            }
        }

        ImageProcessor ip = resultImg.getProcessor().createProcessor(width, height);
        byte[] resultArr = (byte[]) ip.getPixels();
        for (int j = 0; j < medianLists.size(); j++) {
            // convert to int array first so that it sorts correctly since all
            // bytes are signed in Java
            int[] arr = toIntArray((byte[]) medianLists.get(j));
            Arrays.sort(arr);
            resultArr[j] = (byte) arr[arr.length / 2];
            
            //set progress bar
            IJ.showProgress(j, medianLists.size());
        }
        
        resultImg.setProcessor(ip);

        return resultImg;
    }

    /**
     * Converts byte array to int array
     * 
     * @param arr
     * @return
     */
    private static int[] toIntArray(byte[] arr) {
        int[] result = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            result[i] = arr[i] & 0xFF;
        }
        return result;
    }

}
